import * as Cesium from 'cesium'
import {lineString, booleanClockwise} from '@turf/turf';

interface optionsType {
    positions: any,
    height: any,
    bottomImg: string,
    wallImg: string,
    splitNum: number

}

class TerrainClipPlan {
    _viewer: any;
    options: any;
    _positions: any;
    _height: number
    bottomImg: string;
    wallImg: string;
    splitNum: number;
    excavateMinHeight: number
    bottomSurface: any;
    wellWall: any
    wellData: any
    UpTilesModel: any

    constructor(viewer: any, options: optionsType) {
        options = this.setOptions(options)
        this._viewer = viewer;
        this.options = options || {};
        this._positions = options.positions;
        this._height = options.height || 0;
        this.bottomImg = options.bottomImg;
        this.wallImg = options.wallImg;
        this.splitNum = Cesium.defaultValue(options.splitNum, 50);
        this.excavateMinHeight = 0;
        this.bottomSurface = true;
        this.wellWall = null;
        this.wellData = null;
        this.UpTilesModel = null;//剖切在的倾斜摄影上方
        // this._positions && this._positions.length > 0 && this.updateData(this._positions)
    }

    /**
     * 设置或者初始化options
     * @param options
     */
    setOptions(options: optionsType) {
        if (options) {
            return options;
        } else {
            return {
                positions: [[1, 1]],
                height: 1,
                bottomImg: '',
                wallImg: '',
                splitNum: 1
            }
        }
    }

    // startAnalysis() {
    //     this._viewer.scene.globe.clippingPlanes.enabled = true;
    //     this._switchExcavate(true)
    // }

    updateData(pointList: any) {
        this.clear();
        // 对点做处理,如果不是逆时针 变为逆时针
        if (this.ifAntiClockwise(pointList)) {
            pointList.reverse()
        }
        let planes = [];
        let pointLength = pointList.length;
        let a = new Cesium.Cartesian3;
        let n = Cesium.Cartesian3.subtract(pointList[0], pointList[1], a);
        // if (n.x > 0) this.excavateMinHeight = 9999;
        if (n.x !== 0) this.excavateMinHeight = 9999;
        for (let i = 0; i < pointLength; ++i) {

            let nextPointIndex = (i + 1) % pointLength;
            //主要计算解决 excavateMinHeight 的值
            let currPointCartographic = Cesium.Cartographic.fromCartesian(pointList[i]);
            let currPointHeight = this._viewer.scene.globe.getHeight(currPointCartographic) || currPointCartographic.height;
            if (currPointHeight < this.excavateMinHeight) {
                this.excavateMinHeight = currPointHeight
            }
            //计算两个笛卡尔按分量求和
            var midpoint = Cesium.Cartesian3.add(pointList[i], pointList[nextPointIndex], new Cesium.Cartesian3());
           //求中间点
            midpoint = Cesium.Cartesian3.multiplyByScalar(midpoint, 0.5, midpoint);
            // 中心点归一化，忽略长度影响，可以理解坐标归一化后x,y,z 都是各自的方向的单位长度1的向量
            let up = Cesium.Cartesian3.normalize(midpoint, new Cesium.Cartesian3());
            console.log("当前执行到的i", i, pointLength)

            //下一个点与中心点的相减
            let right = Cesium.Cartesian3.subtract(pointList[nextPointIndex], midpoint, new Cesium.Cartesian3());
            right = Cesium.Cartesian3.normalize(right, right);
            //计算（中点向量 以及中点和下一个点的差值的向量  ）垂直于两个向量的一点
            let normal = Cesium.Cartesian3.cross(right, up, new Cesium.Cartesian3());
            normal = Cesium.Cartesian3.normalize(normal, normal);
            //用单位法向量和距离定义一个平面
            let originCenteredPlane = new Cesium.Plane(normal, 0.0);
            //计算中心点到平面的距离
            let distance = Cesium.Plane.getPointDistance(originCenteredPlane, midpoint);

            planes.push(new Cesium.ClippingPlane(normal, distance));

            // //当前点与中心点相减
            // let left = Cesium.Cartesian3.subtract(pointList[i], midpoint, new Cesium.Cartesian3());
            // left = Cesium.Cartesian3.normalize(left, left);
            // //计算（中点向量 以及中点和当前点的差值的向量  ）垂直于两个向量的一点
            // let normalLeff = Cesium.Cartesian3.cross(left, up, new Cesium.Cartesian3());
            // normalLeff = Cesium.Cartesian3.normalize(normalLeff, normalLeff);
            //
            // let originCenteredPlaneLeft = new Cesium.Plane(normalLeff, 0.0);
            // let distanceLeft = Cesium.Plane.getPointDistance(originCenteredPlaneLeft, midpoint);
            //
            // planes.push(new Cesium.ClippingPlane(normalLeff, distanceLeft));
            // planes.push(new Cesium.ClippingPlane(normalLeff, distanceLeft));


        }
        console.log("当前的裁剪区域",planes);
        this._viewer.scene.globe.clippingPlanes = new Cesium.ClippingPlaneCollection({
            planes: planes,
            edgeWidth: 0,
            edgeColor: Cesium.Color.WHITE,
            enabled: true
        })
        if (this.UpTilesModel) {

            const clipTileset = new Cesium.ClippingPlaneCollection({
                planes: planes,
                edgeWidth: 1,
                edgeColor: Cesium.Color.WHITE,
                modelMatrix: Cesium.Matrix4.inverse(
                    Cesium.Transforms.eastNorthUpToFixedFrame(this.UpTilesModel.boundingSphere.center),
                    new Cesium.Matrix4()
                )
            })
            this.UpTilesModel.clippingPlanes = clipTileset
        }
        this._prepareWell(pointList);
        this._createWell(this.wellData);
    }

    clear() {
        if (this._viewer.scene.globe.clippingPlanes) {
            this._viewer.scene.globe.clippingPlanes.enabled = false
            if (!this._viewer.scene.globe.clippingPlanes.isDestroyed()) {
                this._viewer.scene.globe.clippingPlanes.removeAll()
                // this.viewer.scene.globe.clippingPlanes.destroy()
            }
        }
        if (this.UpTilesModel && this.UpTilesModel?.clippingPlanes?.removeAll) {
            this.UpTilesModel.clippingPlanes.removeAll()
        }
        if (this.bottomSurface) {
            this._viewer.scene.primitives.remove(this.bottomSurface)
            delete this.bottomSurface
        }
        if (this.wellWall) {
            this._viewer.scene.primitives.remove(this.wellWall)
            delete this.wellWall
        }
    }

    _prepareWell(e: any) {
        // 一个边分割为多少个点
        let splitNum = this.splitNum;
        let pointListLength = e.length;
        if (0 != pointListLength) {
            for (var a = this.excavateMinHeight - this._height, no_height_top = [], bottom_pos = [], lerp_pos = [], currentIndex = 0; currentIndex < pointListLength; currentIndex++) {
                var nextIndex = currentIndex == pointListLength - 1 ? 0 : currentIndex + 1,
                    currentPoint = Cesium.Cartographic.fromCartesian(e[currentIndex]),
                    nextPoint = Cesium.Cartographic.fromCartesian(e[nextIndex]),
                    currentPointLatlng = [currentPoint.longitude, currentPoint.latitude],
                    nextPointLatlng = [nextPoint.longitude, nextPoint.latitude];

                if (0 == currentIndex) {
                    lerp_pos.push(new Cesium.Cartographic(currentPointLatlng[0], currentPointLatlng[1]))
                    bottom_pos.push(Cesium.Cartesian3.fromRadians(currentPointLatlng[0], currentPointLatlng[1], a))
                    no_height_top.push(Cesium.Cartesian3.fromRadians(currentPointLatlng[0], currentPointLatlng[1], 0));
                }
                for (var p = 1; p <= splitNum; p++) {
                    const lerpPointX = Cesium.Math.lerp(currentPointLatlng[0], nextPointLatlng[0], p / splitNum)
                    const lerpPointY = Cesium.Math.lerp(currentPointLatlng[1], nextPointLatlng[1], p / splitNum);
                    currentIndex == pointListLength - 1 && p == splitNum || (
                        lerp_pos.push(new Cesium.Cartographic(lerpPointX, lerpPointY)),
                            bottom_pos.push(Cesium.Cartesian3.fromRadians(lerpPointX, lerpPointY, a)),
                            no_height_top.push(Cesium.Cartesian3.fromRadians(lerpPointX, lerpPointY, 0)))
                }
            }
            this.wellData = {
                lerp_pos,
                bottom_pos,
                no_height_top
            }
        }
    }

    _createWell(e: any) {
        if (Boolean(this._viewer.terrainProvider._layers)) {
            var t = this;
            this._createBottomSurface(e.bottom_pos);
            var i = Cesium.sampleTerrainMostDetailed(this._viewer.terrainProvider, e.lerp_pos);
            i.then(ies => {
                for (var a = ies.length, n = [], r = 0; r < a; r++) {
                    var s = Cesium.Cartesian3.fromRadians(ies[r].longitude, ies[r].latitude, ies[r].height);
                    n.push(s)
                }
                t._createWellWall(e.bottom_pos, n)
            })
        } else {
            this._createBottomSurface(e.bottom_pos);
            this._createWellWall(e.bottom_pos, e.no_height_top)
        }
    }

    _getMinHeight(e: any) {
        let minHeight = 50000000;
        let minPoint: any = null;
        for (let i = 0; i < e.length; i++) {
            let height = e[i]['z'];
            if (height < minHeight) {
                minHeight = height;
                minPoint = this._ellipsoidToLonLat(e[i]);
            }
        }
        return minPoint.altitude;
    }

    _createBottomSurface(e: any) {
        if (e.length) {
            let minHeight = this._getMinHeight(e);
            let positions = [];
            for (let i = 0; i < e.length; i++) {
                let p = this._ellipsoidToLonLat(e[i]);
                positions.push(p.longitude);
                positions.push(p.latitude);
                positions.push(minHeight);
            }

            let polygon = new Cesium.PolygonGeometry({
                polygonHierarchy: new Cesium.PolygonHierarchy(
                    Cesium.Cartesian3.fromDegreesArrayHeights(positions)
                ),
                perPositionHeight: true,
                closeBottom: false
            });
            let geometry: any = Cesium.PolygonGeometry.createGeometry(polygon);


            var i = new Cesium.Material({
                    fabric: {
                        type: "Image",
                        uniforms: {
                            image: this.bottomImg
                        }
                    }
                }),
                a = new Cesium.MaterialAppearance({
                    translucent: !1,
                    flat: !0,
                    material: i
                });
            this.bottomSurface = new Cesium.Primitive({
                geometryInstances: new Cesium.GeometryInstance({
                    geometry: geometry
                }),
                appearance: a,
                asynchronous: !1
            }), this._viewer.scene.primitives.add(this.bottomSurface)
        }
    }

    _createWellWall(e: any, t: any) {
        let minHeight = this._getMinHeight(e);
        let maxHeights = [];
        let minHeights = [];
        for (let i = 0; i < t.length; i++) {
            maxHeights.push(this._ellipsoidToLonLat(t[i]).altitude);
            minHeights.push(minHeight);
        }
        let wall = new Cesium.WallGeometry({
            positions: t,
            maximumHeights: maxHeights,
            minimumHeights: minHeights,
        });
        let geometry: any = Cesium.WallGeometry.createGeometry(wall);
        var a = new Cesium.Material({
                fabric: {
                    type: "Image",
                    uniforms: {
                        image: this.wallImg
                    }
                }
            }),
            n = new Cesium.MaterialAppearance({
                translucent: !1,
                flat: !0,
                material: a
            });
        this.wellWall = new Cesium.Primitive({
            geometryInstances: new Cesium.GeometryInstance({
                geometry: geometry,
                attributes: {
                    color: Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.GREY)
                },
                id: "PitWall"
            }),
            appearance: n,
            asynchronous: !1
        }), this._viewer.scene.primitives.add(this.wellWall)
    }

    _updateExcavateDepth(e: any) {
        this.bottomSurface && this._viewer.scene.primitives.remove(this.bottomSurface), this.wellWall && this._viewer.scene.primitives.remove(this.wellWall);
        for (var t = this.wellData.lerp_pos, i = [], a = t.length, n = 0; n < a; n++) i.push(Cesium.Cartesian3.fromRadians(t[n].longitude, t[n].latitude, this.excavateMinHeight - e));
        this.wellData.bottom_pos = i, this._createWell(this.wellData), this._viewer.scene.primitives.add(this.bottomSurface), this._viewer.scene.primitives.add(this.wellWall)
    }

    ifAntiClockwise(e: any) {
        //找凸点
        let pointList = JSON.parse(JSON.stringify(e))
        pointList.push(pointList[0])
        let newPoints = pointList.map((point: any, index: number) => {
            return this.getLatLngFromXZY(point)
        })
        //利用turf 创建lineString 的geomtry对象
        let pointListGeo = lineString(newPoints)
        //判断上面创建的点线的顺序是否是顺时针
        let dirRes = booleanClockwise(pointListGeo);
        if (dirRes) {
            console.log("当前绘制图形为顺时针!");
        } else {
            console.log("当前绘制图形为逆时针!");
        }
        return dirRes
    }

    getLatLngFromXZY(e: any) {
        var ellipsoid = this._viewer.scene.globe.ellipsoid;
        var cartesian3 = new Cesium.Cartesian3(e.x, e.y, e.z);
        var cartographic = ellipsoid.cartesianToCartographic(cartesian3);
        var latitude = Cesium.Math.toDegrees(cartographic.latitude);
        var longitude = Cesium.Math.toDegrees(cartographic.longitude);
        var height = cartographic.height;
        return [longitude, latitude]
    }

    // _switchExcavate(e: any) {
    //     e ? (this._viewer.scene.globe.material = Cesium.Material.fromType("WaJue"), this.wellWall.show = !0, this.bottomSurface.show = !0) : (this._viewer.scene.globe.material = null, this.wellWall.show = !1, this.bottomSurface.show = !1)
    // }

    _ellipsoidToLonLat(c: any) {
        let ellipsoid = this._viewer.scene.globe.ellipsoid;
        let cartesian3 = new Cesium.Cartesian3(c.x, c.y, c.z);
        let cartographic = ellipsoid.cartesianToCartographic(cartesian3);
        let lat = Cesium.Math.toDegrees(cartographic.latitude);
        let lng = Cesium.Math.toDegrees(cartographic.longitude);
        let alt = cartographic.height;
        return {
            longitude: lng,
            latitude: lat,
            altitude: alt
        }
    }
}

export default TerrainClipPlan











